package org.ala.tiktools.sharddb.mybatis.pager.parameter;

import org.ala.tiktools.sharddb.exception.ShardDBRuntimeException;
import org.ala.tiktools.sharddb.mybatis.router.ShardInfo;
import org.apache.ibatis.annotations.Param;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 分页相关方法处理器
 *
 * @author ala
 * @date 2025-03-06 16:38
 */
public class ShardPagerMethodHandler {


    /**
     *  包装DataMethod
     */
    public DataMethod dataMethod(Object proxy, Method method, Map<ShardInfo, Object[]> args) {
        return DataMethod.build(proxy, method, args);
    }
    /**
     *  通过DataMethod包装CountMethod
     */
    public CountMethod countMethod(String methodName, DataMethod dataMethod) {
        Method cm = data2Count(methodName, dataMethod.getMethod());

        //  每个参数对应关系，构造count的方法参数
        int[] paramTable = parameterIndexTable(dataMethod.getMethod(), cm);
        Map<ShardInfo, Object[]> args = new HashMap<>();
        dataMethod.getArgs().forEach(((shardInfo, objects) -> {
            Object[] _args = new Object[paramTable.length];
            for (int i = 0; i < paramTable.length; i++) {
                _args[i] = objects[paramTable[i]];
            }
            args.put(shardInfo, _args);
        }));

        return CountMethod.build(dataMethod.getProxy(), cm, args);
    }


    /**
     *  dataMethod与countMethod映射
     */
    protected Map<Method, Method> data2CountMapper = new HashMap<>();
    /**
     *  dataMethod与countMethod映射
     */
    protected Method data2Count(String methodName, Method dataMethod) {
        Method m = data2CountMapper.get(dataMethod);
        if (m == null) {
            synchronized (dataMethod) {
                m = data2CountMapper.get(dataMethod);
                if (m == null) {
                    m = ClassUtils.getMethod(dataMethod.getDeclaringClass(), methodName, null);
                    data2CountMapper.put(dataMethod, m);
                }
            }
        }
        return m;
    }



    /**
     *  countMethod参数下标与dataMethod下标对应表
     */
    protected Map<Method, int[]> parameterIndexMapper = new HashMap<>();
    /**
     *  countMethod参数下标与dataMethod下标对应表
     */
    protected int[] parameterIndexTable(Method dataMethod, Method countMethod) {
        int[] table = parameterIndexMapper.get(dataMethod);
        if (table == null) {
            synchronized (dataMethod) {
                table = parameterIndexMapper.get(dataMethod);
                if (table == null) {
                    List<String> tc = idx2Param(countMethod);
                    Map<String, Integer> td = param2Idx(dataMethod);
                    if (tc.isEmpty() || td.isEmpty()) {
                        throw new ShardDBRuntimeException(String.format("@Pagement注解的方法，count方法和原方法的参数需要通过Param参数的value对应起来。无Param注解无法对应查询条件 method:%s count:%s class:%s", dataMethod.getName(), countMethod.getName(), dataMethod.getDeclaringClass().getSimpleName()));
                    }
                    table = new int[tc.size()];
                    for (int i = 0; i < tc.size(); i++) {
                        Integer idx = td.get(tc.get(i));
                        if (idx == null) {
                            throw new ShardDBRuntimeException(String.format("%s方法中的@Param(\"%s\")参数在%s方法中找不到，无法确定查询条件 class:%s", countMethod.getName(), tc.get(i), dataMethod.getName(), countMethod.getDeclaringClass().getSimpleName()));
                        }
                        table[i] = idx;
                    }
                    parameterIndexMapper.put(dataMethod, table);
                }
            }
        }
        return table;
    }
    /**
     *  取方法每个参数的@Param的value
     */
    protected List<String> idx2Param(Method method) {
        List<String> idx = new ArrayList<>();
        for (int i = 0; i < method.getParameters().length; i++) {
            Parameter p = method.getParameters()[i];
            Annotation[] annos = p.getAnnotations();
            if (annos == null || annos.length == 0) {
                continue;
            }
            String name = null;
            for (Annotation anno : annos) {
                if (anno instanceof Param) {
                    name = ((Param)anno).value();
                }
            }
            if (name == null || name.isEmpty()) {
                continue;
            }
            idx.add(name);
        }
        return idx;
    }
    /**
     *  取方法每个参数的Param的value和小标对应关系
     */
    protected Map<String, Integer> param2Idx(Method method) {
        Map<String, Integer> idx = new HashMap<>();
        for (int i = 0; i < method.getParameters().length; i++) {
            Parameter p = method.getParameters()[i];
            Annotation[] annos = p.getAnnotations();
            if (annos == null || annos.length == 0) {
                continue;
            }
            String name = null;
            for (Annotation anno : annos) {
                if (anno instanceof Param) {
                    name = ((Param)anno).value();
                }
            }
            if (name == null || name.isEmpty()) {
                continue;
            }
            idx.put(name, i);
        }
        return idx;
    }
}
